#pragma once

#include "view_interface.h"
#include "wlroots.hpp"
#include "utils.h"
#include "server.h"

#include <boost/intrusive/list.hpp>
#include <memory>

enum view_type {
    BWL_XDG_SHELL_VIEW,
#ifdef HAVE_XWAYLAND
    BWL_XWAYLAND_VIEW,
#endif
};

class OutputWrapper;

class View
{
    friend class XdgView;
public:
    View(struct wlr_surface* surface, view_type type,
         ViewInterface* view_impl,
         Server* server);

    void set_output(OutputWrapper *output);
    inline OutputWrapper* output() { return m_output; }

    inline void set_workspace(Workspace* w) { m_workspace = w; }

    inline void set_x(int x) { m_x = x; }
    inline void set_y(int y) { m_y = y; }
    inline int x() { return m_x; }
    inline int y() { return m_y; }

    struct wlr_surface* surface_at(double vx, double vy, double* sx, double* sy);
    struct wlr_box geometry();
    void resize(struct wlr_box geo);
    void focus();

    void impl_map();
    void destroy();

private:
    void foreign_toplevel_handle_create();
    void desktop_focus(bool raise);
    struct wlr_box get_surface_geometry();

private:
    void configure(wlr_box geo);
    void close();
    const char *get_string_prop(const char *prop);
    void map();
    void set_fullscreen(bool fullscreen);
    void notify_tiled();
    void unmap(bool client_request);
    void maximize(bool maximized);
    void minimize(bool minimized);
    void move_to_front();
    void move_to_back();
    View *get_root();
    void append_children(wl_array *children);
    bool is_related(wlr_surface *surface);
    view_size_hints get_size_hints();
    view_wants_focus wants_focus();
    bool has_strut_partial();

private:
    void _minimize(bool minimized);
    View* next_toplevel();
    bool is_toplevel();

protected:
    Server* m_server;
    enum view_type m_type;

    std::unique_ptr<ViewInterface> m_impl;
    /*
     * The primary output that the view is displayed on. Specifically:
     *
     *  - For floating views, this is the output nearest to the
     *    center of the view. It is computed automatically when the
     *    view is moved or the output layout changes.
     *
     *  - For fullscreen/maximized/tiled views, this is the output
     *    used to compute the view's geometry. The view remains on
     *    the same output unless it is disabled or disconnected.
     *
     * Many view functions (e.g. view_center(), view_fullscreen(),
     * view_maximize(), etc.) allow specifying a particular output
     * by calling view_set_output() beforehand.
     */
    OutputWrapper *m_output = nullptr;

    Workspace *m_workspace = nullptr;

    struct wlr_surface *m_surface;
    struct wlr_scene_tree *m_scene_tree;
    struct wlr_scene_node *m_scene_node;

    bool m_mapped = false;
    bool m_been_mapped = false;

    bool m_minimized = false;

    struct foreign_toplevel {
        struct wlr_foreign_toplevel_handle_v1 *handle;
        wl_listener_wrapper maximize;
        wl_listener_wrapper minimize;
        wl_listener_wrapper fullscreen;
        wl_listener_wrapper activate;
        wl_listener_wrapper close;
        wl_listener_wrapper destroy;
    } m_toplevel;

    wl_listener_wrapper m_destroy;
    wl_listener_wrapper m_map;
    wl_listener_wrapper m_unmap;
    wl_listener_wrapper m_surface_destroy;
    wl_listener_wrapper m_commit;
    wl_listener_wrapper m_request_move;
    wl_listener_wrapper m_request_resize;
    wl_listener_wrapper m_request_minimize;
    wl_listener_wrapper m_request_maximize;
    wl_listener_wrapper m_request_fullscreen;
    wl_listener_wrapper m_set_title;



    int m_x = 0;
    int m_y = 0;
};
